package resynchronized;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 74646
 * Date: 2022-05-08
 * Time: 9:14
 */


/**
 * 可重入：
 * 直观的说，同一个线程针对同一个锁，连续加锁两次，
 * 如果出现了死锁(死循环)就是不可重入，如果不会死锁就是可重入
 */

class Counter1 {
    // 自增变量
    public int count;

    /**
     * 分析一个线程连续锁两次的情况
     * 外层加了一次锁，内层又对同一个对象再加一次锁
     *
     * 按照死锁的观点分析：
     * 外层锁：进入方法则开始加锁，这次可以加锁成功，当前锁没有被占用
     * 内存锁：进入代码块开始加锁，这次加锁会失败
     *                 因为当前锁被外层锁占用，需要等到外层锁释放后才可以加锁成功
     * 外层锁要执行完整个方法才会释放锁，但是要执行完整个方法，就需要让内层锁加锁成功
     * 这样就会出现死循环的情况（死锁）
     * 如果出现了死锁程序就会出现BUG，所以JVM中将synchronized实现成了可重入锁
     * 对于可重入锁，进行连续加锁操作不会出现死锁的情况。
     * 可重入锁内部会记录当前的锁被哪个线程占用，同时也会记录加锁次数
     *
     * 线程A第一次加锁的时候，可以加锁成功，锁内部就记录了当前的占用者是A，同时加锁次数为1
     * 后续再线程A进行加锁，此时就不是真加锁，而是单纯的将加锁次数自增1
     * （后续的加锁操作没有实质的影响）
     * 后续解锁的时候，先把加锁次数进行自减1，当加锁次数减为0时，就真的解锁了
     *
     * 可重入锁的意义就是降低程序员的负担（降低使用成本，提高了开发效率）
     * 同时也带来了一些代价，程序中需要有更高的开销（维护锁属于哪个线程，并且加减计数，降低了运行效率）
     * 如果使用的是不可重入锁，虽然提高了运行效率，但是降低了开发效率。
     */
    synchronized public void increase(){

        synchronized (this){
            count++;
        }
    }
}

public class Demo1 {
    private static Counter1 counter = new Counter1();

    public static void main(String[] args) {
        Thread t1 = new Thread(()->{
            for (int i = 0; i < 50000; i++) {
                counter.increase();
            }
        });
        Thread t2 = new Thread(()->{
            for (int i = 0; i < 50000; i++) {
                counter.increase();
            }
        });
        t1.start();
        t2.start();

        /**
         * 因为 main 线程与 t1,t2 线程是并发执行的，会导致 t1,t2 线程还没执行完就执行下面的打印操作
         * 为了保证 t1,t2 线程都执行完了再打印 count，
         * 所以调用 join 方法，等待 t1,t2 线程都执行完了再打印
         */
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(counter.count);
    }
}




